一、概念

选择器(selector)是selectableChannel对象的多路复用器,一个selector可以监控多个selectableChannel的io情况,selector可以实现一个线程单独管理多个channel,selector是非阻塞的核心

二、selector可以监控的事件类型

  1. connect 客户端连接服务端事件
  2. accept 服务端接受服务端事件
  3. read 读事件
  4. write 写事件

每次请求都是从客户端连接服务端(connect),服务端开始准备(accept),准备完成后开始读数据,处理完成之后再写数据

三、selector的常用方法

image.png

四、nio 的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//服务端
public class NioServer {
private ServerSocketChannel serverSocketChannel;
private Selector selector;
private Integer port = 8080;

NioServer() {
try {
serverSocketChannel = ServerSocketChannel.open().bind(new InetSocketAddress(port));
//设置非阻塞
serverSocketChannel.configureBlocking(false);
//创建Selector
selector = Selector.open();
//注册tcp 连接事件到 channel
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务端初始化完成,端口号:" + port);
} catch (Exception ex) {
throw new RuntimeException("初始化服务端失败,ex:"+ex);
}
}

private void start() {
try {
//获取查看selector 管理多少个channel
while (selector.select() > 0) {
//走到这里肯定是selector发现channel有变化了,这个大哥把他管理的sekectedKeys拿出来遍历
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//selector 也不知道是什么变化所以就 各种判断一下,是 创建连接、还是准备就绪、还是读取数据、还是写数据、还是关闭连接
if (selectionKey.isConnectable()) {
System.out.println("用户建立连接");
}
if (selectionKey.isAcceptable()) {
System.out.println("用户准备就绪");
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
//这样就拿到了发生变化的 socketChannel

SocketChannel accept = serverSocketChannel.accept();
accept.configureBlocking(false);
//把客户端的channel 注册到 selector 当客户端的socketChannel发生变化出发读逻辑
accept.register(selector, SelectionKey.OP_READ);
}
if (selectionKey.isReadable()) {
System.out.println("读数据");
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
//读数据得是由buffer
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//切换到读模式
StringBuffer sb = new StringBuffer();
while (socketChannel.read(byteBuffer) != -1) {
byteBuffer.flip();
sb.append(StandardCharsets.UTF_8.decode(byteBuffer));
byteBuffer.clear();
}
System.out.println("来自客户端的问候:" + sb.toString());
//来个写数据吧
socketChannel.register(selector,SelectionKey.OP_WRITE);
}
if (selectionKey.isWritable()) {
System.out.println("写数据");
System.out.println("不会写数据");
selectionKey.channel().close();
}
if (selectionKey.isValid()) {
System.out.println("关闭数据");
}
//把当前的selection移除,因为后面事件还会加入
iterator.remove();
}
}
} catch (Exception ex) {
throw new RuntimeException("服务异,ex:", ex);
}


}

public static void main(String[] args) {
//先定义服务端
NioServer nioServer = new NioServer();
//开启服务
nioServer.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//客户端
public class NioClint {

public static void main(String[] args) throws IOException {
SocketChannel open = SocketChannel.open();
open.connect(new InetSocketAddress("127.0.0.1",8080));
open.configureBlocking(false);
ByteBuffer byteBuffer =ByteBuffer.allocate(1024);
byteBuffer.flip();
String msg ="呵呵,你大爷";
byteBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
open.write(byteBuffer);
byteBuffer.clear();
Socket socket = open.socket();
SocketChannel channel = socket.getChannel();
}
}